/******************************************************************************/
/*               (C) 斯凯瑞利（北京）科技有限公司(SKYRELAY)                   */
/*                                                                            */
/* 此代码是斯凯瑞利（北京）科技有限公司为支持客户编写的示例程序的一部分       */
/* 所有使用斯凯瑞利相关芯片的用户可以无偿使用该代码，但需保留本声明部分       */
/* 本代码仅为演示使用，不保证符合所有的应用要求，当有特定规范或需求时，       */
/* 使用者需自行确认代码是否符合要求，不合要求时自行做出相应的修改。           */
/* http://www.skyrelay-ic.com                                                 */
/******************************************************************************/
/**
 ******************************************************************************
 ** 文 件 名： smartoper.c
 **
 ** 文件简述： CPU卡操作演示程序
 **
 ** 版本历史:
 ** 2020-04-20 V1.0.0  sz   发布版
 ******************************************************************************/
#include "iso14443.h"
#include "crypto.h"

sta_result_t FM1208_EraseTest(void);
sta_result_t FM1208_WriteTest(void);
sta_result_t FM1208_ReadTest(void);
// FM1208 使用的部分APDU, 第一字节是后续字节数，后面是APDU内容
uint8_t GetRand[] = {5,0x00,0x84,0x00,0x00,0x04};        // 获取随机数（4字节）
uint8_t ExtAuth[] = {13,0x00,0x82,0x00,0x00,0x08,0,0,0,0,0,0,0,0}; // 外部认证,最后8字节是DES加密后的密钥
uint8_t SelMF[] = {7,0x00,0xA4,0x00,0x00,0x02,0x3F,0x00};    // 选择主目录 3F 00
uint8_t CreatMainKeyFile[] = {12 ,0x80,0xE0,0x00,0x00,0x07,0x3F,0x00,0x6C,0x01,0xFA,0xFF,0xFF}; //建立主目录下密钥文件
uint8_t WriteMainKeyFile[] = {26,0x80,0xD4,0x01,0x00,0x15,0x39,0xF0,0xF0,0xAA,0x33,
                0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
uint8_t CreateADF[] = {18,0x80,0xE0,0x3F,0x01,0x0D,0x38,0x04,0x00,0xF0,0xFA,0x95,
                                0xFF,0xFF,0x41,0x44,0x46,0x30,0x31};    // 建立应用目录（ADF）
uint8_t SelADF[] = {7,0x00,0xA4,0x00,0x00,0x02,0x3F,0x01};   // 选择应用目录3F01
uint8_t CreateADFKeyFile[] = {12,0x80,0xE0,0x00,0x00,0x07,0x3F,0x01,0x00,0x95,0xFA,0xFF,0xFF};
uint8_t WriteADFKeyFile[] = {18,0x80,0xD4,0x01,0x00,0x0D,0x39,0xF0,0xF0,0xAA,0x88,
                            0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};   //写入3F00目录下外部认证密钥
uint8_t CreateBinFile[] = {12,0x80,0xE0,0x00,0x03,0x07,0x28,0x00,0x10,0xFA,0xFA,0xFF,0xFF};//建立应用目录下二进制文件
uint8_t WriteBinFile[] = {13,0x00,0xD6,0x83,0x00,0x08,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}; //写入二进制文件内容
uint8_t ReadBinFile[] = {5,0x00,0xB0,0x83,0x00,0x00}; //选择二进制文件03

uint8_t EraseDF[] = {5,0x80,0x0E,0x00,0x00,0x00}; // 删除目录下所有文件

uint8_t extKey[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};//,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
uint8_t intKey[] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};

/***************************************************************
    擦除FM1208卡片 MF目录下所有的内容（不会擦除MF目录）
    再次使用卡片的时候需要重新建立MF下的应用文件和密钥文件等
    一般卡片厂家提供的都是已经擦除过的，不需要做这个操作
   擦除流程
   1. 选择主目录 MF
   2. 获取随机数并做外部认证（前提卡内已有外部认证密钥）
   3. 擦除所有文件
   4. 建立外部认证密钥文件
   5. 写入外部认证密钥（例子中密钥是 FF FF FF FF FF FF FF FF)
***************************************************************/
sta_result_t FM1208_EraseTest(void)
{
    sta_result_t sta;
    uint8_t i;
    uint8_t tmpBuf[64];
    uint16_t tmpSize;

    uint8_t InOutData[8]={0x00};
    
    setPCDTimeOut(15);
    // 1、选择主目录（MF 3F00）
    //00 A4 00 00 02 3F 00
    SelMF[0] = g_PCB;
    sta = ExchangeData(SelMF,8,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
    }
    else
        return sta;

    //获取随机数
    //00 84 00 00 04
    GetRand[0] = g_PCB;
    sta = ExchangeData(GetRand,6,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
        for(i=0;i<4;i++)
            InOutData[i] = tmpBuf[i+1];// 获得4字节随机数
        for(i=4;i<8;i++)
            InOutData[i] = 0;           // 后续填 0

#if SKY_DEBUG==1
        printf("RM:");
        for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return sta;
    }

    //2. 外部认证
    //00 82 00 00 08 0 0 0 0 0 0 0 0
    des_ecb(ENCRYPT, InOutData, extKey, &ExtAuth[6]);// 加密随机数
    ExtAuth[0] = g_PCB;
    sta = ExchangeData(ExtAuth,14,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("External Auth:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return sta;
    }

    //3. 擦除DF
    //80 0E 00 00 00
    EraseDF[0] = g_PCB;
    sta = ExchangeData(EraseDF,6,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Erase DF:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return sta;
    }
    //4.建立目录下外部认证密钥文件：
    // 80 E0 00 00 07 3F 00 6C 01 FA FF FF
    CreatMainKeyFile[0] = g_PCB;
    sta = ExchangeData(CreatMainKeyFile,13,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Create Main Key:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return Error;
    }
    //5.写入密钥文件
    //
    WriteMainKeyFile[0] = g_PCB;
    sta = ExchangeData(WriteMainKeyFile,27,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Write Main Key File:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return Error;
    }
    return sta;
}
/***************************************************************
    FM1208 CPU卡写卡操作示例
    针对主目录(MF）下已经有外部认证密钥的FM1208卡进行操作，
    如果新擦除的卡片还需要增加创建主目录外部认证密钥和写外部密钥的步骤，详细做法类似5，6步的操作

    写卡流程：
    1、选择主目录（MF 3F00）
    2、外部认证 （使用密钥 FF FF FF FF FF FF FF FF根据建立时的密钥修改)
    3、创建应用目录（ADF 3F01）（例子中目录空间是512字节 0x0400）
    4. 选择应用目录 3F01
    5、创建ADF的秘钥文件（0000）
    6、写入秘钥文件 (例子中密钥是 11 22 33 44 55 66 77 88）
    7、创建应用文件 （例子中文件大小是16字节）
    8、写入数据 （例子中数据是 11 22 33 44 55 66 77 88）
   有错误时显示错误信息
****************************************************************/
sta_result_t FM1208_WriteTest(void)
{
    sta_result_t sta;
    uint8_t i;
    uint8_t tmpBuf[64];
    uint16_t tmpSize;

    uint8_t InOutData[8]={0x00};
    // 1、选择主目录（MF 3F00）
    //00 A4 00 00 02 3F 00
    SelMF[0] = g_PCB;
    sta = ExchangeData(SelMF,8,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
    }
    else
        return sta;

    //获取随机数
    //00 84 00 00 04
    GetRand[0] = g_PCB;
    sta = ExchangeData(GetRand,6,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
        for(i=0;i<4;i++)
            InOutData[i] = tmpBuf[i+1];// 获得4字节随机数
        for(i=4;i<8;i++)
            InOutData[i] = 0;           // 后续填 0

#if SKY_DEBUG==1
        printf("RM:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return sta;
    }

    //2. 外部认证
    //00 82 00 00 08 0 0 0 0 0 0 0 0
    des_ecb(ENCRYPT, InOutData, extKey, &ExtAuth[6]);// 加密随机数
    ExtAuth[0] = g_PCB;
    sta = ExchangeData(ExtAuth,14,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("External Auth:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return sta;
    }

    //3.建立应用文件 ADF, ID = 3F 01, DF = ADF01,
    //80 E0 3F 01 0D 38 04 00 F0 FA 95 FF FF 41 44 46 30 31
    CreateADF[0] = g_PCB;
    sta = ExchangeData(CreateADF,19,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Create ADF:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return Error;
    }
    //4. 选择ADF文件3F01
    //00 A4 00 00 02 3F 01
    SelADF[0] = g_PCB;
    sta = ExchangeData(SelADF,8,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Select ADF:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return Error;
    }
    //5.建立ADF目录下密钥文件：
    // 80 E0 00 00 07 3F 01 00 95 FA FF FF
    CreateADFKeyFile[0] = g_PCB;
    sta = ExchangeData(CreateADFKeyFile,13,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Create ADF Key:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return Error;
    }
    //6.写入密钥文件
    //80 D4 01 00 0D 39 F0 F0 AA 88 1122334455667788
    WriteADFKeyFile[0] = g_PCB;
    sta = ExchangeData(WriteADFKeyFile,19,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Write ADF Key File:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return Error;
    }
    //7.建立应用目录下二进制文件(00 10:16字节大小)：
    // 80 E0 00 03 07 28 00 10 FA FA FF FF
    CreateBinFile[0] = g_PCB;
    sta = ExchangeData(CreateBinFile,13,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Create binary File:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return Error;
    }

    //8.写入二进制文件内容
    // 00 D6 00 00 08 (11 22 33 44 55 66 77 88)
    WriteBinFile[0] = g_PCB;
    sta = ExchangeData(WriteBinFile,14,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Write Binary File:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return Error;
    }

    return sta;
}
/***************************************************************
  读取FM1208中指定的二进制文件
  读取步骤
  1. 选择主文件MF (3F00)
  2. 获取随机数做外部认证 (密钥 FF FF FF FF FF FF FF FF)
  3. 选择应用目录 ADF01 （3F01)
  4. 获取随机数做外部认证 (密钥 11 22 33 44 55 66 77 88)
  5. 读取二进制文件03. 例子是读取全部16字节内容，也可以指定读取固定字节数
***************************************************************/
sta_result_t FM1208_ReadTest(void)
{
    sta_result_t sta;
    uint8_t i;
    uint8_t tmpBuf[64];
    uint16_t tmpSize;
    uint8_t InOutData[8]={0x00};
    //1.选择主文件
    SelMF[0] = g_PCB;
    sta = ExchangeData(SelMF,8,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Select MF:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
        return sta;
    }
    //获取随机数
    GetRand[0] = g_PCB;
    sta = ExchangeData(GetRand,6,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
        for(i=0;i<4;i++)
            InOutData[i] = tmpBuf[i+1];// 获得4字节随机数
        for(i=4;i<8;i++)
            InOutData[i] = 0;           // 后续填 0
#if SKY_DEBUG==1
        printf("Get RM:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return sta;
    }

    //2. 外部认证
    des_ecb(ENCRYPT, InOutData, extKey, &ExtAuth[6]);// 加密随机数
    ExtAuth[0] = g_PCB;
    sta = ExchangeData(ExtAuth,14,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("MF External Auth:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return sta;
    }

    /* 3、选择目录（ADF01） */
    //00 A4 00 00 02 3F 01
    SelADF[0] = g_PCB;
    sta = ExchangeData(SelADF,8,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Select ADF:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
        return sta;

    /*获取随机数*/
    GetRand[0] = g_PCB;
    sta = ExchangeData(GetRand,6,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
        for(i=0;i<4;i++)
            InOutData[i] = tmpBuf[i+1];// 获得4字节随机数
        for(i=4;i<8;i++)
            InOutData[i] = 0;           // 后续填 0

#if SKY_DEBUG==1
        printf("Get RM:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return sta;
    }

    /*4. 外部认证 */
    des_ecb(ENCRYPT, InOutData, intKey, &ExtAuth[6]); // 加密随机数
    ExtAuth[0]=g_PCB;
    sta = ExchangeData(ExtAuth,14,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("ADF External Auth:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return sta;
    }

    //5.读二进制文件03：
    // 00 B0 83 00 00
    ReadBinFile[0] = g_PCB;
    sta = ExchangeData(ReadBinFile,6,tmpBuf,&tmpSize);
    if(sta == Ok)
    {
        PCD_PCB();
#if SKY_DEBUG==1
        printf("Read binary File:");
    	for(i=0;i<tmpSize-2;i++)
            printf("%02X ",tmpBuf[i]);
        printf("\n");
#endif
    }
    else
    {
        sky1311Reset();
    	return sta;
    }
    return sta;
}

/***************************************************************
    CPU卡操作示例
   读取CPU卡的UID等，并使用部分APDU做测试
   寻卡等操作时，显示操作结果
   有错误时显示错误信息
****************************************************************/
sta_result_t smartTypeA_test(void)
{
    sta_result_t sta;
    uint8_t i, tmpBuf[100];
    uint16_t tmpSize;
    //uint8_t sw1, sw2;
    #if SKY_DEBUG==1
    printf("\nSmart Type A test\n");
    #endif
    sta = piccRATS(0x80,tmpBuf,&tmpSize);
    if(sta == NoResponse)
    {
        #if SKY_DEBUG==1
        printf("\r\nRATS error!\n");
        #endif
        return ErrorAts;
    }
    else{ 
        #if SKY_DEBUG==1
        printf("ATS : ");
        for(i=0;i<tmpSize-2;i++)
            printf("%2X ",tmpBuf[i]);
        printf("\n");
        #endif
    }
    sta = FM1208_ReadTest();
    return Ok;
}
